using System;
using System.ComponentModel;
using System.Configuration;
using Common.Core;
using Common.Core.Objects;
using Common.Unity.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Guard=Common.Core.Guard;
using InjectionMember=Microsoft.Practices.Unity.InjectionMember;

namespace Common.Unity.Extensions.Configuration
{
	public class PolicyReferenceElement : InjectionMemberElement, IObjectWithKey
	{
		const string PolicyName = "policy";

		public override string ElementName
		{
			get { return PolicyName; }
		}

		[ConfigurationProperty( ElementTypeName, DefaultValue = null, IsRequired = false, IsKey = false )]
		public string ElementType
		{
			get { return (string)this[ElementTypeName]; }
			set { this[ElementTypeName] = value; }
		}	const string ElementTypeName = "elementType";

		/// <summary>
		/// The actual <see cref="System.Type"/> object for the 
		/// type this element is registering.
		/// </summary>
		public Type Type
		{
			get { return TypeResolver.ResolveType( TypeName ); }
		}

		/// <summary>
		/// Source type to configure in the container.
		/// </summary>
		[ConfigurationProperty("type", IsRequired = true)]
		public string TypeName
		{
			get { return (string)this["type"]; }
			set { this["type"] = value; }
		}

		public override InjectionMember CreateInjectionMember()
		{
			var policy = CreatePolicy();
			return new PolicyInjection( Type, policy );
		}

		public IBuilderPolicy CreatePolicy()
		{
			return (IBuilderPolicy)Container.Resolve( Type, Name );
		}

		protected IUnityContainer Container
		{
			get { return container ?? ( container = ResolveContainer() ); }
		}	IUnityContainer container;
		
		object IObjectWithKey.Key
		{
			get
			{ 
				var result = !string.IsNullOrEmpty( Name ) ? string.Format( "{0}:{1}", Name, TypeName ) : TypeName;
				return result;
			}
		}

		static IUnityContainer ResolveContainer()
		{
			var container = ThreadLocalStorage.Peek<IUnityContainer>();
			Microsoft.Practices.Unity.Utility.Guard.ArgumentNotNull( container, "container" );
			var common = container.Configure<CommonExtension>();
			if ( common != null )
			{
				var configuration = container.Configure<IConfigurationExtensionConfiguration>();
				if ( configuration != null )
				{
					return container;
				}
				throw new InvalidOperationException( "IConfigurationExtensionConfiguration not found in specified container.  PolicyReferenceElement cannot configure properly." );
			}
			throw new InvalidOperationException( "CommonExtension not found in specified container.  PolicyReferenceElement cannot configure properly." );
		}
	}
}